Transport level retry - #954
Conversation
swallez
left a comment
There was a problem hiding this comment.
Left some comments on implementation independence and async tasks.
Adding this to TransportOptions is an interesting choice, as it allows using different retry policies with a single transport.
8012980 to
2fd0bbc
Compare
be599ac to
ba8a26b
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces transport-level retry support in the Java client by adding a configurable RetryConfig (backoff + retryable statuses/exceptions) and routing requests through a new RetryingHttpClient wrapper when retries are enabled (globally or per-request). It also updates the low-level client adapters so status-carrying exceptions (e.g., ResponseException) can be classified by HTTP status for retry decisions.
Changes:
- Add
RetryConfigandTransportOptions#retryConfig()to configure retry behavior at the client or request level. - Introduce
RetryingHttpClientand integrate it intoElasticsearchTransportBasewith lazy, shared wrapping. - Add/extend tests to validate retry behavior (status-based, exception-based, cancellation) across mocked and real HTTP clients.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| java-client/src/test/java/co/elastic/clients/transport/TransportRetryStatusTest.java | New integration tests validating status-based retries via real low-level clients. |
| java-client/src/test/java/co/elastic/clients/transport/http/RetryingHttpClientTest.java | New unit tests for retry classification and retry/cancellation behavior. |
| java-client/src/test/java/co/elastic/clients/transport/ElasticsearchTransportRetryTest.java | New tests validating transport-level routing (client-level vs per-request retry config). |
| java-client/src/test/java/co/elastic/clients/transport/ElasticsearchTransportConfigTest.java | Adds coverage for the new builder shortcut wiring retry config into transport options. |
| java-client/src/main/java/co/elastic/clients/transport/TransportOptions.java | Adds retryConfig() default + builder hooks to carry retry config through request options. |
| java-client/src/main/java/co/elastic/clients/transport/RetryConfig.java | New retry configuration type (backoff + retryable statuses/exceptions). |
| java-client/src/main/java/co/elastic/clients/transport/rest5_client/Rest5ClientOptions.java | Persists/copies retry config into rest5 transport options. |
| java-client/src/main/java/co/elastic/clients/transport/rest5_client/Rest5ClientHttpClient.java | Exposes status codes from rest5 ResponseException for retry classification. |
| java-client/src/main/java/co/elastic/clients/transport/rest_client/RestClientOptions.java | Persists/copies retry config into legacy rest transport options. |
| java-client/src/main/java/co/elastic/clients/transport/rest_client/RestClientHttpClient.java | Exposes status codes from legacy ResponseException for retry classification. |
| java-client/src/main/java/co/elastic/clients/transport/http/TransportHttpClient.java | Adds responseStatusCode(Throwable) hook for wrapper-agnostic status extraction. |
| java-client/src/main/java/co/elastic/clients/transport/http/RetryingHttpClient.java | New retrying wrapper implementation (sync/async + backoff scheduling + cancellation). |
| java-client/src/main/java/co/elastic/clients/transport/ElasticsearchTransportConfig.java | Adds builder shortcuts to configure retries via the top-level client builder. |
| java-client/src/main/java/co/elastic/clients/transport/ElasticsearchTransportBase.java | Routes requests through a shared retry wrapper when retries are enabled. |
| java-client/src/main/java/co/elastic/clients/transport/DefaultTransportOptions.java | Adds retry config storage and builder support in the default options implementation. |
| java-client/src/main/java/co/elastic/clients/transport/BackoffPolicy.java | Fixes minor doc typos. |
Comments suppressed due to low confidence (2)
java-client/src/main/java/co/elastic/clients/transport/http/RetryingHttpClient.java:242
isRetryableFailureonly checkserrand onegetCause()level for a status-carrying exception. If the low-level client'sResponseExceptionis wrapped more deeply (common with async frameworks), the retry decision can fall back to exception-type matching and incorrectly retry / not retry. Consider walking the full cause chain when extracting a status code.
Integer status = delegate.responseStatusCode(err);
if (status == null && err.getCause() != null) {
status = delegate.responseStatusCode(err.getCause());
}
if (status != null) {
java-client/src/main/java/co/elastic/clients/transport/http/RetryingHttpClient.java:250
RetryConfigdocs say wrapped exceptions are considered, butisRetryableExceptiononly checks the exception itself and a single cause. This can miss retryable causes nested deeper in the chain (e.g.,CompletionException -> RuntimeException -> IOException). Iterate through the full cause chain until null/self-reference.
boolean isRetryableException(RetryConfig config, Throwable err) {
return matchesRetryable(config, err) || matchesRetryable(config, err.getCause());
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
* rewriting global retry, addressing review * refactoring retrying logic * add logs * minimal test refactor * add configurable exception logic * comments * code style * simplify test * single request retry * bugfixes, fetching inner status code * refactor, simplify * better comments * more simplifying comments * fix rebase * address copilot review Co-authored-by: Laura Trotta <153528055+l-trotta@users.noreply.github.com>
Adds new retry functionality to the client, configurable like so:
Or if only a single request needs to be retried:
The delegate client (
Rest5Client,RestClientor any custom client) is wrapped in an instance ofRetryingHttpClient, which takes care of the retrying logic according to the configuration.This only concerns the same node the request was originally sent to, it's still the underlying client's responsibility to handle dead node logic and node selection.